package cn.yuyiling.jelly.qa.service.impl;

import cn.yuyiling.jelly.kg.mongodb.entity.KnowledgePoint;
import cn.yuyiling.jelly.kgai.api.KnowledgeAIService;
import cn.yuyiling.jelly.qa.api.AnswerService;
import cn.yuyiling.jelly.qa.api.CommentService;
import cn.yuyiling.jelly.qa.api.QuestionService;
import cn.yuyiling.jelly.qa.dto.QuestionDetails;
import cn.yuyiling.jelly.qa.mongodb.entity.Answer;
import cn.yuyiling.jelly.qa.mongodb.entity.Comment;
import cn.yuyiling.jelly.qa.mongodb.entity.Question;
import cn.yuyiling.jelly.qa.mongodb.repository.QuestionRepository;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@DubboService
public class QuestionServiceImpl implements QuestionService {

    private static final Logger logger = LoggerFactory.getLogger(QuestionServiceImpl.class);

    private static final String KEY_PREFIX = "view:question:";
    private static final String SUMMARY_KEY = "view:question:top24h";

    private static final String KEY_PREFIX_POPULAR = "like:question:";
    private static final String SUMMARY_KEY_POPULAR = "like:question:top24h";

    @Autowired
    private QuestionRepository questionRepository;
    @Autowired
    private AnswerService answerService;
    @Autowired
    private CommentService commentService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @DubboReference
    private KnowledgeAIService knowledgeAIService;

    @Override
    public Question createQuestion(Question question) {
        question.setCreatedAt(new Date());
        Question savedQuestion = questionRepository.save(question);
        Question extractedQuestion = knowledgeAIService.extractKnowledgePoint(savedQuestion);
        Question evaluatedQuestion = knowledgeAIService.evaluateDifficultyOfQuestion(extractedQuestion);
        return questionRepository.save(evaluatedQuestion);
    }

    @Override
    public Question updateQuestion(String id, Question updatedQuestion) {
        Optional<Question> optionalQuestion = questionRepository.findById(id);
        if(optionalQuestion.isPresent()){
            Question question = optionalQuestion.get();
            question.setContent(updatedQuestion.getContent());
            question.setKnowledgePoints(updatedQuestion.getKnowledgePoints());
            question.setMetadata(updatedQuestion.getMetadata());
            question.setUpdatedAt(new Date());
            return questionRepository.save(question);
        } else {
            throw new RuntimeException("问题不存在，ID: " + id);
        }
    }

    @Override
    public Question likeQuestion(String id) {
        return questionRepository.findById(id)
                .map(item -> {
                    item.setLikes(item.getLikes() + 1);
                    return questionRepository.save(item);
                })
                .orElseThrow(() -> new RuntimeException("Question not found"));
    }

    @Override
    public Question dislikeQuestion(String id) {
        return questionRepository.findById(id)
                .map(item -> {
                    item.setDislikes(item.getDislikes() + 1);
                    return questionRepository.save(item);
                })
                .orElseThrow(() -> new RuntimeException("Question not found"));
    }

    @Override
    public void deleteQuestionAndRelatedData(String questionId) {
        // 删除问题
        questionRepository.deleteById(questionId);

        // 删除与该问题相关的所有回答
        answerService.deleteAnswersByQuestionId(questionId);

        // 删除与该问题相关的所有评论
        commentService.deleteCommentsByQuestionId(questionId);
    }

    @Override
    public long getTotalQuestionCount() {
        return questionRepository.count();
    }

    @Override
    public List<Question> getHottestQuestions(int limit) {
        return questionRepository.findHottestQuestions(limit);
    }

    @Override
    public List<Question> getRecommendedQuestions(List<String> knowledgePoints) {
        return questionRepository.findRecommendedQuestions(knowledgePoints);
    }

    @Override
    public List<Question> getQuestionsByKnowledgePoint(String knowledgePoint) {
        return questionRepository.findByKnowledgePointsContaining(knowledgePoint);
    }

    @Override
    public QuestionDetails getQuestionDetails(String questionId) {
        Question question = questionRepository.findById(questionId)
                .orElseThrow(() -> new RuntimeException("Question not found"));

        List<Answer> answers = answerService.getAnswersByQuestionId(questionId);
        List<Comment> comments = commentService.getCommentsByQuestionId(questionId);

        return new QuestionDetails(question, answers, comments);
    }

    @Override
    public void addKnowledgePoint(String id, String knowledgePoint) {
        Question question = questionRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Question not found"));
        if (!question.getKnowledgePoints().contains(knowledgePoint)) {
            question.getKnowledgePoints().add(knowledgePoint);
            questionRepository.save(question);
        }
    }

    @Override
    public Question getQuestionById(String id) {
        Optional<Question> optionalQuestion = questionRepository.findById(id);
        if(optionalQuestion.isPresent()){
            Question question = optionalQuestion.get();

            return question;
        } else {
            throw new RuntimeException("问题不存在，ID: " + id);
        }
    }


    @Override
    public Question viewQuestionById(String id) {
        String hourKey = getHourKey(LocalDateTime.now());
        if (!redisTemplate.hasKey(hourKey)) {
            redisTemplate.opsForZSet().add(hourKey, id, 0);
            redisTemplate.expire(hourKey, Duration.ofHours(25));
        }
        redisTemplate.opsForZSet().incrementScore(hourKey, id, 1);
        return getQuestionById(id);
    }

    private String getHourKey(LocalDateTime time) {
        return KEY_PREFIX + time.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
    }

    @Override
    public List<String> get24HourTopViewQuestion(int count) {
        Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet()
                .reverseRangeWithScores(SUMMARY_KEY, 0, count - 1);
        if (set == null) return Collections.emptyList();
        List<String> result = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> item : set) {
            result.add(item.getValue() + " (" + item.getScore().intValue() + ")");
        }
        return result;
    }

    @Override
    public void refreshTop24hViewQuestionRanking() {
        logger.info("start refresh Top 24 hours View Question Ranking");
        List<String> keys = new ArrayList<>();
        for (int i = 0; i < 24; i++) {
            keys.add(getHourKey(LocalDateTime.now().minusHours(i)));
        }
        redisTemplate.opsForZSet().unionAndStore(keys.get(0), keys.subList(1, keys.size()), SUMMARY_KEY);
        redisTemplate.expire(SUMMARY_KEY, Duration.ofHours(1));
    }

    @Override
    public Question likeQuestionById(String id) {
        String likeHourKey = getLikeHourKey(LocalDateTime.now());
        if (!redisTemplate.hasKey(likeHourKey)) {
            redisTemplate.opsForZSet().add(likeHourKey, id, 0);
            redisTemplate.expire(likeHourKey, Duration.ofHours(25));
        }
        redisTemplate.opsForZSet().incrementScore(likeHourKey, id, 1);
        return likeQuestion(id);
    }

    private String getLikeHourKey(LocalDateTime time) {
        return KEY_PREFIX_POPULAR + time.format(DateTimeFormatter.ofPattern("yyyyMMddHH"));
    }

    @Override
    public List<String> get24HourTopPopularQuestion(int count) {
        Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet()
                .reverseRangeWithScores(SUMMARY_KEY_POPULAR, 0, count - 1);
        if (set == null) return Collections.emptyList();
        List<String> result = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> item : set) {
            result.add(item.getValue() + " (" + item.getScore().intValue() + ")");
        }
        return result;
    }

    @Override
    public void refreshTop24hPopularQuestionRanking() {
        logger.info("start refresh Top 24 hours Popular Question Ranking");
        List<String> keys = new ArrayList<>();
        for (int i = 0; i < 24; i++) {
            keys.add(getLikeHourKey(LocalDateTime.now().minusHours(i)));
        }
        redisTemplate.opsForZSet().unionAndStore(keys.get(0), keys.subList(1, keys.size()), SUMMARY_KEY_POPULAR);
        redisTemplate.expire(SUMMARY_KEY_POPULAR, Duration.ofHours(1));
    }

    @Override
    public List<Question> findByKnowledgePointsIn(List<String> knowledgePoints) {
        return questionRepository.findByKnowledgePointsIn(knowledgePoints);
    }

}